#include "DataProcessor.h"
#include <QtSql>
#include "Profiler.h"

// Data Processor

static const unsigned long CYGNUM_HAL_RTC_NUMERATOR = 1000000000; // nanoseconds_per_X
static const unsigned long CYGNUM_HAL_RTC_DENOMINATOR = 100;      // ticks_per_X
static const unsigned long CYGNUM_HAL_RTC_PERIOD = 250000;        // hallclocks_per_tick

DataProcessor::DataProcessor()
: nsPerTick(CYGNUM_HAL_RTC_NUMERATOR / CYGNUM_HAL_RTC_DENOMINATOR)
, psPerHalClock(nsPerTick * 1000 / CYGNUM_HAL_RTC_PERIOD)
, halClocksPerTick(CYGNUM_HAL_RTC_PERIOD)
{
}

void DataProcessor::process()
{
    processThreadMapping();
    processEvents();
    processMutexes();
}

QSet<DataItem*> DataProcessor::itemsInEventRange(int location, int length) const
{
    QSet<DataItem*> items;
    if (eventRangeArray && eventRangeArrayIndex) 
    {
        unsigned int start = 0;
        if (location > 0)
        {
            if (location - 1 < eventRangeArrayIndexSize)
            {
                start = eventRangeArrayIndex[location - 1];
            }
            else
            {
                qDebug() << "itemsInEventRange() : location >= eventRangeArrayIndexSize :" << location << eventRangeArrayIndexSize;
                start = eventRangeArrayIndex[eventRangeArrayIndexSize - 1];
            }
        }
        unsigned int end = 0;
        if (location + length - 1 > 0)
        {
            if (location + length - 1 < eventRangeArrayIndexSize)
            {
                end = eventRangeArrayIndex[location + length - 1];
            }
            else
            {
                qDebug() << "itemsInEventRange() : location + length >= eventRangeArrayIndexSize :" << location << length << eventRangeArrayIndexSize;
                end = eventRangeArrayIndex[eventRangeArrayIndexSize - 1];
            }
        }
        if (end >= eventRangeArraySize)
        {
            qDebug() << "itemsInEventRange() : end >= eventRangeArraySize" << end << eventRangeArraySize;
            end = eventRangeArraySize - 1;
        }
        items.reserve(end - start + 1);
        for (unsigned int pos = start; pos <= end; pos++)
            items.insert(eventRangeArray[pos]);
    }
    return items;
}

QSet<DataItem*> DataProcessor::itemsInTimeRange(unsigned long long location, unsigned long long length) const
{
    int eventStartLocation = floorEventForTime(location);
    int eventEndLocation = ceilEventForTime(location + length);
    return itemsInEventRange(eventStartLocation, eventEndLocation - eventStartLocation);
}

int DataProcessor::floorEventForTime(unsigned long long time) const
{
    if (timeEventMap.contains(time))
        return timeEventMap[time];
    
    QMap<unsigned long long, int>::const_iterator iter = timeEventMap.lowerBound(time);
    if (iter != timeEventMap.begin())
        iter--;
    return iter.value();
}

int DataProcessor::ceilEventForTime(unsigned long long time) const
{
    QMap<unsigned long long, int>::const_iterator iter = timeEventMap.lowerBound(time);
    if ((iter == timeEventMap.end()) && (iter != timeEventMap.begin()))
        iter--;
    return iter.value();
}

void DataProcessor::compileEventRangeMap()
{
    if (eventRangeArray)
        delete [] eventRangeArray;
    if (eventRangeArrayIndex)
        delete [] eventRangeArrayIndex;
    
    eventRangeArrayIndexSize = totalEvents();
    eventRangeArrayIndex = new unsigned int[eventRangeArrayIndexSize];
    
    eventRangeArraySize = 0;
    for (int location = 0; location < eventRangeArrayIndexSize; location++) {
        eventRangeArraySize += eventRangeMap.values(location).size();
        eventRangeArrayIndex[location] = eventRangeArraySize;
    }
    
    eventRangeArray = new DataItem*[eventRangeArraySize];
    unsigned int i = 0;
    for (int location = 0; location < eventRangeArrayIndexSize; location++) {
        QList<DataItem*> values = eventRangeMap.values(location);
        for (int value = 0; value < values.size(); value++)
            eventRangeArray[i + value] = values.at(value);
        i += values.size();
    }
}

double DataProcessor::calcEventEntropyRating(double eventsPerSec) const
{
    return 0.0;
}

double DataProcessor::eventEntropyInEventRange(int location, int length) const
{
    unsigned long long startTime = timeEventMap.key(location);
    unsigned long long endTime = timeEventMap.key(location + length);
    unsigned long long timeInterval = endTime - startTime;
    return calcEventEntropyRating((double)length / (double)timeInterval);
}

double DataProcessor::eventEntropyInTimeRange(unsigned long long location, unsigned long long length) const
{
    int eventStartLocation = floorEventForTime(location);
    int eventEndLocation = ceilEventForTime(location + length);
    int eventCount = eventEndLocation - eventStartLocation;
    return calcEventEntropyRating((double)eventCount / (double)length);
}

QString DataProcessor::threadName(int id) const
{
    return idThreadMapping[id]->name;
}

int DataProcessor::totalEvents() const
{
    return eventRangeMap.keys().count();
}

int DataProcessor::totalThreads() const
{
    return idThreadMapping.keys().count();
}

bool createdBefore(const ThreadMapping* left, const ThreadMapping* right)
{
	if (left->id == 4095)
		return true;
	
	if (right->id == 4095)
		return false;
	
	return left->id < right->id;
}

QList<ThreadMapping*> DataProcessor::getThreads() const
{
	QList<ThreadMapping*> threads = idThreadMapping.values();
	qSort(threads.begin(), threads.end(), createdBefore);
	return threads;
}

void DataProcessor::processThreadMapping()
{
    Profiler profiler("DataProcessor::processThreadMapping");
    
    QSqlQuery threadMappingQuery("select distinct _threadId, _threadPointer, _threadSymbol from threadMapping");
    bool columnIndicesLoaded = false;
    int _threadId, _threadPointer, _threadSymbol;
    while (threadMappingQuery.next()) {
        if (!columnIndicesLoaded) {
            QSqlRecord record = threadMappingQuery.record();
            _threadId = record.indexOf("_threadId");
            _threadPointer = record.indexOf("_threadPointer");
            _threadSymbol = record.indexOf("_threadSymbol");
            columnIndicesLoaded = true;
        }
        
        int threadId = threadMappingQuery.value(_threadId).toInt();
        unsigned long threadPointer = threadMappingQuery.value(_threadPointer).toUInt();
        QString threadSymbol = threadMappingQuery.value(_threadSymbol).toString();
        
        ThreadMapping* threadMapping = new ThreadMapping(threadId, threadPointer, threadSymbol);
        idThreadMapping[threadId] = threadMapping;
        pointerThreadMapping[threadPointer] = threadMapping;
    }
}

void DataProcessor::processEvents()
{
    Profiler profiler("DataProcessor::processEvents");
    
    // Initialise instance variables
    currentEventNr = 0;
    currentRunningThreadId = -1;
    bool handlingInterrupts = false;
	
    unsigned long long ticks = 0;
    
    QSqlQuery eventQuery("select * from event");
    
    int e = 0;
    absoluteTimestampOffset = 0;
    int _eventNr, _class, _type, _timestamp, _threadId, _createdThreadPointer, _awakenedThreadPointer, _suspendedThreadPointer, _resumedThreadPointer, _modifiedThreadPointer, _delay, _delayedThreadPointer, _enteredThreadPointer, _replacingThreadPointer, _currentThreadPointer;
    int eNr = 0;
    while (eventQuery.next()) {
        // Load the column indices and update event counter
        if (e == 0) {
            QSqlRecord record = eventQuery.record();
            _eventNr = record.indexOf("_eventNr");
            _class = record.indexOf("_class");
            _type = record.indexOf("_type");
            _timestamp = record.indexOf("_timestamp");
            _threadId = record.indexOf("_threadId");
            _createdThreadPointer = record.indexOf("_createdThreadPointer");
            _awakenedThreadPointer = record.indexOf("_awakenedThreadPointer");
            _suspendedThreadPointer = record.indexOf("_suspendedThreadPointer");
            _resumedThreadPointer = record.indexOf("_resumedThreadPointer");
            _modifiedThreadPointer = record.indexOf("_modifiedThreadPointer");
            _delay = record.indexOf("_delay");
            _delayedThreadPointer = record.indexOf("_delayedThreadPointer");
            _enteredThreadPointer = record.indexOf("_enteredThreadPointer");
            _replacingThreadPointer = record.indexOf("_replacingThreadPointer");
            _currentThreadPointer = record.indexOf("_currentThreadPointer");
        }
        ++e;
        
        QString eClass = eventQuery.value(_class).toString();
        if (eClass == "empty")
        {
          continue;
        }
        
        eNr = eventQuery.value(_eventNr).toInt();
        QString eType = eventQuery.value(_type).toString();
        unsigned long long eTimestamp = eventQuery.value(_timestamp).toULongLong();
        int threadId = eventQuery.value(_threadId).toInt();
        
        // Update the number of the currently processed event
        currentEventNr = eNr;

/*        
        // Map the absolute timestamp to the event number (timestamp in msec)
        double tmpAbsoluteTimestamp = ((double)ticks * (double)nsPerTick + ((double)eTimestamp * (double)psPerHalClock) / 1000.0) / 1000.0;
        unsigned long long absoluteTimestamp = (unsigned long long)tmpAbsoluteTimestamp;
*/
        // Map the absolute timestamp to the event number (timestamp in halclocks)
        unsigned long long absoluteTimestamp = ticks * halClocksPerTick + eTimestamp + absoluteTimestampOffset;
        if (absoluteTimestamp < previousAbsoluteTimestamp) {
            qDebug() << "event " << eNr << " @ " << absoluteTimestamp << " halclocks and previous " << previousAbsoluteTimestamp << " halclocks";
            qDebug() << "halclocks since last tick : prev " << previousTimestamp << " curr " << eTimestamp;
            qDebug() << "ticks : prev " << previousTick << " curr " << ticks;
            absoluteTimestampOffset = absoluteTimestampOffset + (previousAbsoluteTimestamp - absoluteTimestamp);
            qDebug() << "setting timestamp offset to " << absoluteTimestampOffset << " halclocks";
            absoluteTimestamp = ticks * halClocksPerTick + eTimestamp + absoluteTimestampOffset;
            qDebug() << "now timestamp = " << absoluteTimestamp << " halclocks";
        }
        previousAbsoluteTimestamp = absoluteTimestamp;
        previousTimestamp = eTimestamp;
        previousTick = ticks;
		
        timeEventMap[absoluteTimestamp] = eNr;
        eventTimeMap[eNr] = absoluteTimestamp;
        
        // Update the tick counter (tick start holds the new rtc value, tick end marks the timestamp reset)
        if (eClass == "clock" && eType == "isr") 
        {
//            qDebug() << "tick increase event " << eNr;
            if (eTimestamp > 1.5 * halClocksPerTick)
            {
              // In the beginning, the clock tick interrupts don't appear yet, so the timestamp keeps going up.
              // The 1.5 factor is just a guess. This might need tweaking.
              absoluteTimestampOffset = eTimestamp - halClocksPerTick;
              qDebug() << "timestamp at CLOCK_ISR > 1.5 * expected value, setting absoluteTimestampOffset to " << absoluteTimestampOffset;
            }
            ticks++;
        }
        
        // Add the event item
        DataEventItem* eventItem = new DataEventItem(eNr, eClass, eType);
        commitDataEventItem(eventItem);
        
        
        /////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Thread handling
        
        if (eClass == "thread") {
            if (eType == "create") {
                unsigned long pointer = eventQuery.value(_createdThreadPointer).toUInt();
                createThread(pointer, threadId);
            }
            else if (eType == "sleep") {
                sleepThread(threadId);
            }
            else if (eType == "wake") {
                unsigned long pointer = eventQuery.value(_awakenedThreadPointer).toUInt();
                wakeThread(pointer, threadId);
            }
            else if (eType == "suspend") {
                unsigned long pointer = eventQuery.value(_suspendedThreadPointer).toUInt();
                suspendThread(pointer, threadId);
            }
            else if (eType == "resume") {
                unsigned long pointer = eventQuery.value(_resumedThreadPointer).toUInt();
                resumeThread(pointer, threadId);
            }
            else if (eType == "priority") {
                unsigned long pointer = eventQuery.value(_modifiedThreadPointer).toUInt();
                changeThreadPriority(pointer, threadId);
            }
            else if (eType == "delay") {
                int delay = eventQuery.value(_delay).toInt();
                unsigned long pointer = eventQuery.value(_delayedThreadPointer).toUInt();
                delayThread(pointer, delay, threadId);
            }
            else if (eType == "alarm") {
                checkAlarm(threadId);
            }
            else if (eType == "enter") {
                unsigned long pointer = eventQuery.value(_enteredThreadPointer).toUInt();
                enterThread(pointer);
            }
            else if (eType == "switch") {
                unsigned long currentPointer = eventQuery.value(_currentThreadPointer).toUInt();
                unsigned long replacingPointer = eventQuery.value(_replacingThreadPointer).toUInt();
                switchThread(currentPointer, replacingPointer);
            }
        }
        
		
        /////////////////////////////////////////////////////////////////////////////////////////////////////////
        // DSR handling
		
        if (eClass == "interrupt") 
        {
            if (eType == "start dsr") \
            {
                setCurrentRunningThreadId(getInterruptThreadMap()->id);
                
                startInterruptHandling();
                handlingInterrupts = true;
            }
            else if (eType == "stop dsr") 
            {
                handlingInterrupts = false;
            }
        }
            
        if (!handlingInterrupts) {
          // The thread id of the event indicates which thread is running
          setCurrentRunningThreadId(threadId);
        }
    }
    
    
    // add an extra event-time pair at (last_event+1), necessary for time-based view
    eventTimeMap[eNr + 1] = previousAbsoluteTimestamp;
    
    finalizeThreadStates();
    compileEventRangeMap();
}

void DataProcessor::createThread(unsigned long threadPointer, int currentThreadId)
{
    ThreadMapping* createdThreadMap = getThreadMapping(threadPointer);
    
    // Commit a create thread state
    DataThreadStateItem* createState = new DataThreadStateItem(createdThreadMap, "created", currentEventNr, 1);
#if 0
    qDebug() << "DataProcessor::createThread " << createState->state \
             << " " << createState->eventLocation \
             << " " << createState->eventLength \
             << " " << createState->threadMapping->id \
             << " " << createState->threadMapping->pointer \
             << " " << createState->threadMapping->name;
#endif
    commitDataThreadStateItem(createState);
    
    // Created threads are initialized with their suspend counter set to 1 and a sleep counter set to 0
    threadSuspendCounter[createdThreadMap->id] = 1;
    threadSleepCounter[createdThreadMap->id] = 0;
	
    DataThreadStateItem* suspendState = new DataThreadStateItem(createdThreadMap, "suspended", currentEventNr + 1);
    idThreadStateMapping[createdThreadMap->id] = suspendState;
}

void DataProcessor::updateThreadState(ThreadMapping* threadMap, int eventNr)
{
  QString stateText = "runnable";
  if (threadSleepCounter[threadMap->id] > 0)
  {
    stateText = "sleeping";
  }
  else if (threadSuspendCounter[threadMap->id] > 0)
  {
    stateText = "suspended";
  }
  setThreadState(threadMap->id, new DataThreadStateItem(threadMap, stateText, eventNr));
}

void DataProcessor::sleepThread(int threadId)
{
  ThreadMapping* sleepThreadMap = getThreadMapping(threadId);
  if (sleepThreadMap)
  {
    threadSleepCounter[sleepThreadMap->id]++;
    updateThreadState(sleepThreadMap, currentEventNr);
  }
}

void DataProcessor::wakeThread(unsigned long awakenedThreadPointer, int currentThreadId)
{
  ThreadMapping* awakenedThreadMap = getThreadMapping(awakenedThreadPointer);
  if (awakenedThreadMap)
  {
    threadSleepCounter[awakenedThreadMap->id]--;
    updateThreadState(awakenedThreadMap, currentEventNr);
  }
}

void DataProcessor::suspendThread(unsigned long suspendedThreadPointer, int currentThreadId)
{
  ThreadMapping* suspendedThreadMap = getThreadMapping(suspendedThreadPointer);
  if (suspendedThreadMap)
  {
    threadSuspendCounter[suspendedThreadMap->id]++;
    updateThreadState(suspendedThreadMap, currentEventNr);
  }
}

void DataProcessor::resumeThread(unsigned long resumedThreadPointer, int currentThreadId)
{
  ThreadMapping* resumedThreadMap = getThreadMapping(resumedThreadPointer);
  if (resumedThreadMap)
  {
    threadSuspendCounter[resumedThreadMap->id]--;
    updateThreadState(resumedThreadMap, currentEventNr);
  }
}

void DataProcessor::changeThreadPriority(unsigned long modifiedThreadPointer, int newPriority)
{
}

void DataProcessor::delayThread(unsigned long delayedThreadPointer, int delay, int currentThreadId)
{
/*
    ThreadMapping* delayedThreadMap = getThreadMapping(delayedThreadPointer);
    setThreadState(delayedThreadMap->id, new DataThreadStateItem(delayedThreadMap, "delayed", currentEventNr));
*/
}

void DataProcessor::checkAlarm(int threadId)
{
}

void DataProcessor::enterThread(unsigned long enteredThreadPointer)
{
/*
    ThreadMapping* enteredThreadMap = getThreadMapping(enteredThreadPointer);
    setThreadState(enteredThreadMap->id, new DataThreadStateItem(enteredThreadMap, "entered", currentEventNr));
*/
}

void DataProcessor::switchThread(unsigned long currentThreadPointer, unsigned long replacingThreadPointer)
{
}

void DataProcessor::startInterruptHandling()
{
	setThreadState(getInterruptThreadMap()->id, new DataThreadStateItem(getInterruptThreadMap(), "running", currentEventNr));
}

void DataProcessor::finalizeThreadState(int threadId)
{
    if (!idThreadStateMapping.contains(threadId))
        return;
    
    DataThreadStateItem* state = idThreadStateMapping[threadId];
    state->eventLength = currentEventNr - state->eventLocation;
#if 0
    qDebug() << "DataProcessor::finalizeThreadState(" << threadId << ") " << state->state \
             << " " << state->eventLocation \
             << " " << state->eventLength \
             << " " << state->threadMapping->id \
             << " " << state->threadMapping->pointer \
             << " " << state->threadMapping->name;
#endif
    commitDataThreadStateItem(state);
    idThreadStateMapping.remove(threadId);
}

void DataProcessor::setThreadState(int threadId, DataThreadStateItem* state)
{
	// Avoid concatenating two identical states
	if (idThreadStateMapping.contains(threadId) && (idThreadStateMapping[threadId]->state == state->state))
		return;
	
    finalizeThreadState(threadId);
    idThreadStateMapping[threadId] = state;
}

ThreadMapping* DataProcessor::getThreadMapping(int threadId)
{
    ThreadMapping* map;
    if (!idThreadMapping.contains(threadId)) 
    {
        map = new ThreadMapping(threadId);
        idThreadMapping[threadId] = map;
    }
    else 
    {
        map = idThreadMapping[threadId];
        // collapse multiple ids for same pointer into 1
        if (map->pointer != 0)
        {
            unsigned long threadPointer = map->pointer;
            if (pointerThreadMapping.contains(threadPointer))
            {
                map = pointerThreadMapping[threadPointer];
            }
        }
    }
    return map;
}

ThreadMapping* DataProcessor::getThreadMapping(unsigned long threadPointer)
{
    ThreadMapping* map;
    if (!pointerThreadMapping.contains(threadPointer)) {
        // TODO: read symbol from elf
        map = new ThreadMapping(-1, threadPointer, "unknown");
        pointerThreadMapping[threadPointer] = map;
    }
    else {
        map = pointerThreadMapping[threadPointer];
    }
    return map;
}

void DataProcessor::setCurrentRunningThreadId(int threadId)
{
  if (currentRunningThreadId == threadId)
  {
    return;
  }
    
  // Finalize the last running thread state (avoid this for interrupt run states)
  if (currentRunningThreadId != -1)
  {
    if (currentRunningThreadId != -16)
    {
      ThreadMapping* currentRunningThreadMap = getThreadMapping(currentRunningThreadId);
      updateThreadState(currentRunningThreadMap, currentEventNr);
    }
    else 
    {
      finalizeThreadState(currentRunningThreadId);
    }
  }

  // Create a thread id mapping if necessary
  ThreadMapping* map = getThreadMapping(threadId);
  
  // Create a new running thread state
  DataThreadStateItem* state = new DataThreadStateItem(map, "running", currentEventNr);
  setThreadState(map->id, state);
  
  currentRunningThreadId = threadId;
}

void DataProcessor::finalizeThreadStates()
{
    virtualLastEventNr = currentEventNr + 1;
    
    QMapIterator<int, DataThreadStateItem*> iter(idThreadStateMapping);
    while (iter.hasNext()) {
        iter.next();
        DataThreadStateItem* state = iter.value();
        state->eventLength = virtualLastEventNr - state->eventLocation;
#if 0
        qDebug() << "DataProcessor::finalizeThreadStates " << state->state \
                 << " " << state->eventLocation \
                 << " " << state->eventLength \
                 << " " << state->threadMapping->id \
                 << " " << state->threadMapping->pointer \
                 << " " << state->threadMapping->name;
#endif
        commitDataThreadStateItem(state);
    }
}

void DataProcessor::commitDataEventItem(DataEventItem* item)
{
    eventRangeMap.insertMulti(item->eventNr, item);
}

void DataProcessor::commitDataThreadStateItem(DataThreadStateItem* item)
{
    for (int eNr = item->eventLocation; eNr < item->eventLocation + item->eventLength; ++eNr)
        eventRangeMap.insertMulti(eNr, item);
}

void DataProcessor::processMutexes()
{
  Profiler profiler("DataProcessor::processMutexes");
  
  // Initialise instance variables
  currentEventNr = 0;
  currentRunningThreadId = -1;
  
  unsigned long long ticks = 0;
  
  QSqlQuery mutexQuery("select * from mutex");
  
  int e = 0;
  int _eventNr, _pointer, _event, _result, _onThreadId, _symbol, _state, _timestamp;
  while (mutexQuery.next()) 
  {
    // Load the column indices and update event counter
    if (e == 0) 
    {
      QSqlRecord record = mutexQuery.record();
      _eventNr = record.indexOf("_eventNr");
      _pointer = record.indexOf("_pointer");
      _event = record.indexOf("_event");
      _result = record.indexOf("_result");
      _onThreadId = record.indexOf("_onThreadId");
      _symbol = record.indexOf("_symbol");
      _state = record.indexOf("_state");
      _timestamp = record.indexOf("timestamp");
    }
    ++e;
    
    int eNr = mutexQuery.value(_eventNr).toInt();
    QString symbol = mutexQuery.value(_symbol).toString();
    QString event = mutexQuery.value(_event).toString();
    QString state = mutexQuery.value(_state).toString();
    unsigned long long eTimestamp = mutexQuery.value(_timestamp).toULongLong();
    int threadId = mutexQuery.value(_onThreadId).toInt();
    
    ThreadMapping* map = getThreadMapping(threadId);

    // Update the number of the currently processed event
    currentEventNr = eNr;
    

    /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Mutex handling

    if (event == "lock" && state != "locked")
    {
      lockMutex(symbol, map->id);
    }
    else if (event == "unlock" && state == "unlocked")
    {
      unlockMutex(symbol, map->id);
    }
  }
  
  finalizeMutexes();
}

void DataProcessor::lockMutex(const QString& symbol, int threadId)
{
  if (!idMutexLevelMap.contains(threadId))
  {
    idMutexLevelMap[threadId] = 1;
  }
  else
  {
    idMutexLevelMap[threadId]++;
  }

  // Create an item for the mutex and leave it open ended
  DataMutexItem* item = new DataMutexItem(symbol, idMutexLevelMap[threadId], currentEventNr);
  idOpenMutexesMap[threadId][symbol] = item;
}

bool hasHigherLevel(const DataMutexItem* left, const DataMutexItem* right)
{
  return left->level > right->level;
}

void DataProcessor::unlockMutex(const QString& symbol, int threadId)
{
  if (!idOpenMutexesMap.contains(threadId) || !idOpenMutexesMap[threadId].contains(symbol))
  {
    return;
  }

  // Close the open mutex item
  DataMutexItem* item = idOpenMutexesMap[threadId][symbol];
  idOpenMutexesMap[threadId].remove(symbol);
  item->setEventEndLocation(currentEventNr);

  // Update the mutex level for the thread
  QList<DataMutexItem*> openMutexes = idOpenMutexesMap[threadId].values();
  if (openMutexes.size() > 0) 
  {
    qSort(openMutexes.begin(), openMutexes.end(), hasHigherLevel);
    idMutexLevelMap[threadId] = openMutexes.first()->level;
  }
  else 
  {
    idMutexLevelMap.remove(threadId);
  }

  idMutexesMap[threadId].append(item);
}

void DataProcessor::finalizeMutexes()
{
  QMapIterator<int, QMap<QString, DataMutexItem*> > threadIter(idOpenMutexesMap);
  while (threadIter.hasNext()) 
  {
    threadIter.next();
    QListIterator<DataMutexItem*> openMutexesIter(threadIter.value().values());
    while (openMutexesIter.hasNext()) 
    {
      DataMutexItem* item = openMutexesIter.next();
      item->setEventEndLocation(virtualLastEventNr);
      idMutexesMap[threadIter.key()].append(item);
    }
  }
}


// Data Item

DataItem::DataItem(int eventNr)
: eventNr(eventNr)
{
}


// Data Event Item

DataEventItem::DataEventItem(int eventNr, const QString& eventClass, const QString& eventType)
: DataItem(eventNr), eventClass(eventClass), eventType(eventType)
{
}


// Data Thread State Item

DataThreadStateItem::DataThreadStateItem(ThreadMapping* threadMapping, const QString& state, int eventLocation, int eventLength)
: DataItem(eventLocation), threadMapping(threadMapping), state(state), eventLocation(eventLocation), eventLength(eventLength)
{
}


// Data Mutex Item

DataMutexItem::DataMutexItem(const QString& symbol, int level, int eventLocation, int eventLength)
: DataItem(eventLocation), symbol(symbol), level(level), eventLocation(eventLocation), eventLength(eventLength)
{
}
